'''
https://www.xiaohongshu.com/web-login/captcha?redirectPath=https%3A%2F%2Fwww.xiaohongshu.com%2Fsearch_result%2F64411035000000002701173d&callFrom=web&biz=sns_web&verifyUuid=0a9d9192-9473-4b42-bf0a-1da5e6b0a004*zENojNZR&verifyType=102&verifyBiz=461
'''
import os

from loguru import logger
import execjs
from views.xhs import identify
import base64
import requests
import json
import hashlib
import binascii
import string
import time
import random
from urllib.parse import urlparse, urlencode, unquote_plus
from Crypto.Cipher import DES, AES, DES3
from Crypto.Util.Padding import pad, unpad


def get_distance(target_bytes, bg_bytes):
    # 小红书距离识别 直接出距离
    distance = identify.single_discern(
        target_bytes, bg_bytes, 'out.png'
    )
    # 缩放
    return int(distance * 0.7723577235772358)


def my_des_descrpy(ciphertext, data_str):
    key = data_str.encode("utf-8")
    ciphertext = base64.b64decode(ciphertext)
    des = DES.new(key=key, mode=DES.MODE_ECB)
    bytes = des.decrypt(ciphertext)
    return unpad(bytes, des.block_size).decode()


def my_des_encrypt(plaintext, data_str):
    """ 核心加密算法 """
    key = data_str.encode("utf-8")
    des = DES.new(key=key, mode=DES.MODE_ECB)
    plaintext = pad(plaintext.encode("utf-8"), des.block_size)
    bytes = des.encrypt(plaintext)
    return base64.b64encode(bytes).decode()


def get_tracks(dis):
    """
    易盾轨迹
    :param dis:
    :return:
    """
    track = [[0, 0, 0]]
    x = random.randint(0, 10)  # 移动X坐标
    y = random.randint(0, 5)  # 移动y坐标
    pass_time = random.randint(10, 90)  # 移动时间
    move_dis = x  # 移动距离
    track.append([x, 0, pass_time])

    # 模拟开始拖动时会产生0-3个类似的x相同的轨迹
    for _ in range(random.randint(0, 3)):
        pass_time += random.randint(2, 5)  # 移动时间
        y = random.randint(0, 5)  # 移动y坐标
        track.append([x, 0, pass_time])

    # 开始模拟轨迹，一般开始拖动时会很快，越接近目标点越慢
    while move_dis < dis:
        if move_dis > dis * 0.95:
            offset = random.randint(-1, 1)  # 模拟快到目标点时调整滑块时的轨迹，一般会左右移动几下对齐
        elif move_dis > dis * 0.85:
            offset = random.randint(0, 1)
        elif move_dis > dis * 0.75:
            offset = random.randint(1, 2)
        elif move_dis > dis * 0.6:
            offset = random.randint(2, 4)
        else:
            offset = random.randint(5, 8)

        pass_time += random.randint(30, 48)
        move_dis += offset
        y = random.choice([y, random.randint(-5, 5)])  # 移动y坐标
        track.append([move_dis, 0, pass_time])

    # 模拟停止时会产生0-3个类似的x相同的轨迹
    for _ in range(random.randint(0, 3)):
        pass_time += random.randint(2, 5)  # 移动时间
        y = random.randint(0, 5)  # 移动y坐标
        track.append([move_dis, y, pass_time])

    return track, dis


def get_dy_tracks(distance):
    """
    获取轨迹参数
    """
    import random
    tracks = list()
    y, v, t, current = 0, 0, 1, 0
    mid = distance * 3 / 4
    exceed = random.randint(40, 90)
    z = random.randint(30, 150)
    while current < (distance + exceed):
        if current < mid / 2:
            a = 2
        elif current < mid:
            a = 3
        else:
            a = -3
        a /= 2
        v0 = v
        s = v0 * t + 0.5 * a * (t * t)
        current += int(s)
        v = v0 + a * t
        y += random.randint(-3, 3)
        z = z + random.randint(5, 10)
        tracks.append([min(current, (distance + exceed)), y, z])
    while exceed > 0:
        exceed -= random.randint(0, 5)
        y += random.randint(-3, 3)
        z = z + random.randint(5, 9)
        tracks.append([min(current, (distance + exceed)), y, z])
    tr = [
        [0, 0, 0]
    ]
    _y = 0
    for i, x in enumerate(tracks):
        if i % 20 == 0:
            _y += 1
        tr.append([
            x[0], _y, x[2]
        ])
    tr.append([
        distance,
        tr[-1][1],
        tr[-1][-1] + 10
    ])
    return tr


def dumps(data_dict):
    """
    字典数据序列化
    :return:
    """
    return json.dumps(data_dict, separators=(',', ':'), ensure_ascii=False)


def get_a1():
    PLATFORM_CODE = {
        "Windows": 0,
        "iOS": 1,
        "Android": 2,
        "MacOs": 3,
        "Linux": 4,
        "other": 5
    }

    def get_platform_code(platform):
        return PLATFORM_CODE.get(platform, PLATFORM_CODE["other"])

    def gen_random_string(length):
        CHARSET = string.ascii_lowercase + string.digits  # 小写字母和数字
        return ''.join(random.choice(CHARSET) for i in range(length))

    def crc32_string(s):
        return binascii.crc32(s.encode('utf-8')).to_bytes(4, byteorder='big').hex()

    def generate_local_id(platform):
        LOCAL_ID_SECRET_VERSION = '0'
        platform_code = get_platform_code(platform)
        timestamp_hex = str(hex(int(
            time.time() * 1e3
        ))[2:])
        random_string = gen_random_string(30)
        combined_string = timestamp_hex + random_string + str(platform_code) + LOCAL_ID_SECRET_VERSION + "000"
        crc_value = crc32_string(combined_string)
        final_id = combined_string + crc_value
        return final_id[:52]

    c = ""
    l = 0
    while l < 1e2 and len(c) != 52:
        c = generate_local_id('Windows')
        l += 1
    return c


def get_current_timestamp13():
    """
    获取13位数时间戳
    :return:
    """
    return "{}".format(int(time.time() * 1e3))


def my_md5(encrypt_data) -> str:
    """
    md5摘要算法加密
    :return:
    """
    obj = hashlib.md5()
    obj.update(encrypt_data.encode('utf-8'))
    return obj.hexdigest()


def get_xs_xt(interface_url, data_dict, cookies, data_type='params'):
    """ xs核心加密 """
    if data_type == "params":
        md5_str = f'url={urlparse(interface_url).path}?{unquote_plus(urlencode(data_dict))}'
    else:
        md5_str = f'url={urlparse(interface_url).path}{dumps(data_dict)}'

    x1 = my_md5(md5_str)
    x_t = get_current_timestamp13()
    # x_t =1716387279164
    fp_plaintext = f"x1={x1};x2=0|0|0|1|0|0|1|0|0|0|1|0|0|0|0;x3={cookies['a1']};x4={x_t};"
    payload = ctx.call('getPayload', fp_plaintext)
    x_s_dict = {"signSvn": "51",
                "signType": "x1",
                "appId": cookies['xsecappid'],
                "signVersion": "1",
                "payload": payload}
    data_str = json.dumps(x_s_dict, separators=(',', ':'), ensure_ascii=False)
    x_s = f"XYW_{base64.b64encode(data_str.encode()).decode()}"
    return {
        'x-s': x_s,
        'x-t': x_t,
    }


def save_image(content, file_path):
    try:
        with open(file_path, "wb") as file:
            file.write(content)
        # logger.success(f"图片保存成功：{file_path}")
    except Exception as e:
        # logger.error(f"图片保存失败：{file_path} {e}")
        pass


def get_headers_dict(data_dict, cookies) -> dict:
    """ 更新小红书全餐 """
    x6 = data_dict['x-t']
    x7 = data_dict['x-s']

    # x6 = 1719588666748
    # x7 = "XYW_eyJzaWduU3ZuIjoiNTEiLCJzaWduVHlwZSI6IngxIiwiYXBwSWQiOiJsb2dpbiIsInNpZ25WZXJzaW9uIjoiMSIsInBheWxvYWQiOiI0NjRkYmIwZTgzZGY1MmU5NzdiMTUyNDViODliOGY2ZDY0NGViMWMyZDM1MGNlY2U5YWExYjBiYzYyMmFiNzM2YTMxZGZkOGI4MjQ1YzQ0YWM4NzllZDE3ZWRjODE1MTBjOWUzYmZkYTFmYWExZWI5MGQ3NGFhMzFiNTRjNzJjZDBkNzRhYTMxYjU0YzcyY2RhYzQ4OWI5ZGE4Y2U1ZTQ4ZjRhZmI5YWNmYzNlYTI2ZmUwYjI2NmE2YjRjYzNjYjU4ZjI4M2JjOWQ2OTg3NDAyNzMzYTU1OTg2MDVkZDU1MWJjYzhjOGIwYWVlYWNiMmFhMzcyZjQ1NWE5MTA4MzA2Mzc2ODg4NzliNDczMTEwYjIxOTdkNjZmYjQ3YTZjMDczYWZkNWMxNWFhZDkwMDNhYTg0ZjgwNmE4NDU1OWU5NTMyYTM1YzA4MmM3ODc1ZDg1ZmJhZDY0YWNmMjMzMjU0MWE3NmFhMWUxM2UwM2QzYzUzZmZjYTNmYzc0OGY3ODIifQ=="

    x8 = 'I38rHdgsjopgIvesdVwgIC+oIELmBZ5e3VwXLgFTIxS3bqwErFeexd0ekncAzMFYnqthIhJed9MDKutRI3KsYorWHPtGrbV0P9WfIi/eWc6eYqtyQApPI37ekmR1QL+5Ii6sdnoeSfqYHqwl2qt5B0DoIvMzOZQqZVw7IxOeTqwr4qtiIkrOIi/skccxICLdI3Oe0utl2ADZsLveDSKsSPw5IEvsiutJOqw8BVwfPpdeTDWOIx4VIiu6ZPwbPut5IvlaLbgs3qtxIxes1VwHIkumIkIyejgsY/WTge7sjutKrZgedWI9gfKe0zqnI3gsfVtnIEeejutvmzi1NgDtIx5e1qt3bm6e198LIEDbIiE44dzMI3h+8IYgIEhIBVwSIChV895sWW0s6VwTIv3eSn6ej/k2Ii/e3DhSPqwDIkes3BJs6Sgs3uwze7/sDVtCIxiaZnJsdqwka7KefPwmI3YrI3Y5/qtJrPtq/AiNKYdsjjdsVqtOIioeY/NsxzLTIkdsfFJs6j4kIh7s6qw0IkS2pPwpyuwvIvNsxutVIE7siqwlIh/eDqtUtqwPmVwDICcdI3l2GfuA4z/e1utVIEScIi5eSuwoBZcAO00e0I=='

    x9 = ctx.call('getX9', f'{x6}{x7}{x8}')
    data_obj = {
        "s0": 5,
        "s1": "",
        "x0": "1",
        "x1": "3.7.4",
        "x2": "Windows",
        "x3": cookies['xsecappid'],
        "x4": "0.6.6",
        "x5": cookies['a1'],
        "x6": int(x6),
        "x7": x7,
        "x8": x8,
        "x9": x9,
        "x10": 1
    }
    x_s_common = ctx.call('fetchXSCommon', data_obj)

    res_dict = {
        'x-s': data_dict['x-s'],
        'x-t': data_dict['x-t'],
        'x-s-common': x_s_common
    }
    return res_dict


core_encrypt_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "core_encrypt.js")
ctx = execjs.compile(open(core_encrypt_path, encoding="utf-8").read())

headers = {
    "Accept": "application/json, text/plain, */*",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive",
    "Content-Type": "application/json;charset=UTF-8",
    "Origin": "https://www.xiaohongshu.com",
    "Pragma": "no-cache",
    "Referer": "https://www.xiaohongshu.com/",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-site",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
}
cookies = {
    "a1": "1905e44624798p8vrx6ye7zw2m048xkda9fwyy81o50000426883",
    "webId": "528d0ae548ad258286c6fb07c2787907",
    "gid": "yj82d44KWq0Dyj82d44KJjvY4WjYUYhFCKvdiylWuEJ68E284YCTfv8884JKYYq8Ky48KDjy",
    "abRequestId": "528d0ae548ad258286c6fb07c2787907",
    "webBuild": "4.23.1",
    "web_session": "030037a14ddc2e14602416ce06214a017a7b47",
    "unread": "{%22ub%22:%22665937970000000014018e9e%22%2C%22ue%22:%22665a9c3100000000150120d6%22%2C%22uc%22:26}",
    "xsecappid": "login",
    "acw_tc": "6850926d25bddd77237dfef04a134f22ec6ae6497dc3b487ee44f9cce4613da3",
    "websectiga": "9730ffafd96f2d09dc024760e253af6ab1feb0002827740b95a255ddf6847fc8",
    "sec_poison_id": "d1c563d4-9cf2-4450-bdd7-c8adabb130d6"
}


class XhsCaptcha():

    def __init__(self, verify_uuid,
                 register_url="https://edith.xiaohongshu.com/api/redcaptcha/v2/captcha/register",
                 check_url="https://edith.xiaohongshu.com/api/redcaptcha/v2/captcha/check"):
        self.verifyUuid = verify_uuid
        self.register_url = register_url
        self.check_url = check_url
        self.des_key_dict = {
            'width_mouse_key': 'WquqhEkd',  # 加密key mouseEnd width
            'time_key': 'vPMvCY4K',  # 加密key time
            'track_key': 'PYrm8rMk',  # 加密key time
            'captach_key': '76a2171c',
        }

    def pass_slide(self):
        width = 286
        verifyUuid = self.verifyUuid
        data = {
            "secretId": "000",
            "verifyType": "102",
            "verifyUuid": verifyUuid,
            "verifyBiz": "461",
            "biz": "sns_web",
            "sourceSite": "https://www.xiaohongshu.com/search_result/64411035000000002701173d",
            "captchaVersion": "1.1.2"
        }
        xs_info_dict = get_xs_xt(self.register_url, data, cookies, data_type='data')
        headers_updated = get_headers_dict(xs_info_dict, cookies)
        headers.update(headers_updated)

        response = requests.post(self.register_url, headers=headers, data=dumps(data), cookies=cookies)
        data_dict = response.json()
        code = data_dict['code']
        if code != 0:
            logger.error(data_dict)
            raise Exception('获取图片状态码失败！')

        rid = data_dict['data']['rid']
        captchaInfo = data_dict['data']['captchaInfo']
        # logger.info(f'captchaInfo: {captchaInfo}')

        img_dict = json.loads(my_des_descrpy(captchaInfo, self.des_key_dict['captach_key']))
        logger.info(f'图片信息: {img_dict}')
        bg_url = img_dict['backgroundUrl']
        target_url = img_dict['captchaUrl']

        target_bs = requests.get(target_url, headers=headers, cookies=cookies).content
        bg_bs = requests.get(bg_url, headers=headers, cookies=cookies).content
        # save_image(target_bs, 'target.png')
        # save_image(bg_bs, 'bg.png')
        distance = get_distance(target_bs, bg_bs)
        logger.info(f'滑动距离: {distance}')

        # 两种轨迹都能过!
        # tracks,distance= get_tracks(distance)
        tracks = get_dy_tracks(distance)
        time_str = tracks[-1][-1] + 300
        logger.info(f'轨迹: {tracks}')
        # time.sleep(random.uniform(1, 2))

        # 数据加密
        width_str = my_des_encrypt(str(width), self.des_key_dict['width_mouse_key'])
        distance_str = my_des_encrypt(str(distance), self.des_key_dict['width_mouse_key'])

        time_str = my_des_encrypt(str(time_str), self.des_key_dict['time_key'])
        track_str = my_des_encrypt(dumps(tracks), self.des_key_dict['track_key'])
        captcha_info = {'mouseEnd': distance_str,  # 滑动距离
                        'time': time_str,  # time_str
                        'track': track_str,  # 轨迹
                        'width': width_str}

        logger.info(f'captcha_info: {captcha_info}')

        data2 = {
            "rid": rid,
            "checkCount": 1,
            "captchaInfo": dumps(captcha_info)
        }
        data2.update(data)
        data2.pop('secretId')

        xs_info_dict = get_xs_xt(self.check_url, data2, cookies, data_type='data')
        headers_updated = get_headers_dict(xs_info_dict, cookies)
        headers.update(headers_updated)
        response = requests.post(self.check_url, headers=headers, data=dumps(data2), cookies=cookies)
        data_dict = response.json()
        # logger.info(response.text)
        return data_dict


if __name__ == '__main__':
    obj = XhsCaptcha(verify_uuid="0a9d9192-9473-4b42-bf0a-1da5e6b0a004*zENojNZR")
    logger.info(
        obj.pass_slide()
    )
